package com.javaio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Objects;
import java.util.Set;

/**
 * @program: study
 * @className: SingleReactor
 * @description: TODO
 * @author: kukuxiahuni
 * @create: 2019-11-19 16:11
 * @version: v1.0
 **/
public class SingleReactor {


    private static final class Reactor implements Runnable {

        final Selector selector;
        final ServerSocketChannel serverSocketChannel;
        final int port;

        public Reactor(int port) throws IOException {
            this.port = port;
            this.selector = Selector.open();
            this.serverSocketChannel = ServerSocketChannel.open();
            this.serverSocketChannel.socket().bind(new InetSocketAddress(port));
            this.serverSocketChannel.configureBlocking(false);

            SelectionKey selectionKey = this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
            selectionKey.attach(new Acceptor());
        }


        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    this.selector.select();
                    Set<SelectionKey> selectionKeySet = this.selector.selectedKeys();
                    selectionKeySet.forEach(
                            this::dispatch
                    );
                    selectionKeySet.clear();

                }
            } catch (IOException e) {
                e.printStackTrace();
            }


        }


        void dispatch(SelectionKey key) {
            Runnable r = (Runnable) key.attachment();
            if (Objects.nonNull(r)) {
                r.run();
            }
        }


        class Acceptor implements Runnable {

            @Override
            public void run() {

                try {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    if (Objects.nonNull(socketChannel)) {
                        new Handler(selector, socketChannel);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 功能描述: 处理读写
     *
     * @param:
     * @return:
     * @auther: kukuxiahuni
     * @date: 2019/11/19 5:57 下午
     * @modify_auther: kukuxiahuni
     * @modify_time: 2019/11/19 5:57 下午
     **/
    private static final class Handler implements Runnable {

        /**
         * 与客户端建立的channel
         */
        final SocketChannel socketChannel;
        final SelectionKey selectionKey;

        final int READING = 0, WRITING = 1;
        int state = READING;

        ByteBuffer input = ByteBuffer.allocate(1024);
        ByteBuffer output = ByteBuffer.allocate(1024);

        public Handler(Selector selector, SocketChannel socketChannel) throws IOException {
            this.socketChannel = socketChannel;
            this.socketChannel.configureBlocking(false);
            this.selectionKey = this.socketChannel.register(selector, SelectionKey.OP_READ);
            this.selectionKey.attach(this);
            this.selectionKey.interestOps(SelectionKey.OP_READ);
            selector.wakeup();
        }

        @Override
        public void run() {

            if (state == READING) {

                try {
                    read();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            } else {
                try {
                    send();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        /**
         * 功能描述: 是否读完
         *
         * @param:
         * @return:
         * @auther: kukuxiahuni
         * @date: 2019/11/19 6:04 下午
         * @modify_auther: kukuxiahuni
         * @modify_time: 2019/11/19 6:04 下午
         **/
        boolean inputIsComplete() {
            return true;
        }

        /**
         * 功能描述: 发送是否完成
         *
         * @param:
         * @return:
         * @auther: kukuxiahuni
         * @date: 2019/11/19 6:06 下午
         * @modify_auther: kukuxiahuni
         * @modify_time: 2019/11/19 6:06 下午
         **/
        boolean outputIsComplete() {
            return true;
        }


        /**
         * 功能描述: 读消息
         *
         * @param: k
         * @return:
         * @auther: kukuxiahuni
         * @date: 2019/11/19 6:02 下午
         * @modify_auther: kukuxiahuni
         * @modify_time: 2019/11/19 6:02 下午
         **/
        void read() throws IOException {
            this.socketChannel.read(input);
            if (inputIsComplete()) {
                //process(
                //
                this.state = WRITING;
                this.selectionKey.interestOps(SelectionKey.OP_WRITE);
            }
        }

        void send() throws IOException {
            this.socketChannel.write(output);
            if (outputIsComplete()) {
                this.selectionKey.cancel();

            }
        }
    }


}
